/*
 * @Date: 2022-03-22 14:26:10
 * @LastEditors: 春贰
 * @gitee: https://gitee.com/chun22222222
 * @github: https://github.com/chun222
 * @Desc:
 * @LastEditTime: 2022-08-06 09:15:49
 * @FilePath: \server\system\core\db\mysql_databackup.go
 */
package db

import (
	"errors"
	"fmt"
	"io/ioutil"

	"go2admin_pure/system/core/config"
	"go2admin_pure/system/util/file"
	"go2admin_pure/system/util/sys"

	//"github.com/mholt/archiver"  //压缩
	"io"
	"os"
	"strings"
	"time"
)

// TPLSqlModel 导出数据sql部分
type TPLSqlModel struct {
	TableName string // 表名
	CreateSQL string // 创建表sql语句
	InsertSQL string // 插入数据sql语句
}

// CreateTable 创建table sql查询
type CreateTable struct {
	Table       string
	CreateTable string `gorm:"column:Create Table"`
}

type TabelInfo struct {
	Field   string
	Type    string
	Null    string
	Key     string
	Default string
	Extra   string
}

const SplitStr string = "$#$@!$2$" //特殊分隔 不然分隔出问题

// Import 导入
func Import(sqlfile string) (err error) {

	if !file.CheckNotExist(sqlfile) {
		sqls, _ := ioutil.ReadFile(sqlfile)
		sqlArr := strings.Split(string(sqls), SplitStr)

		for _, sql := range sqlArr {

			if sql == "" {

				continue
			}
			//fmt.Println(sql)
			if err := Instance().Exec(sql).Error; err != nil {
				return err
			}

		}
	} else {
		return errors.New("not exist")
	}
	return nil
}

// Export 导出数据库所有表
func Export(tables []string, isExportdata bool, desc string, type_ string) (outFile string, err error) {

	// 创建导出sql文件
	filename := time.Now().Format("20060102150405.999999")
	outDir := fmt.Sprintf("%s/%s/%s", sys.ExecutePath(), strings.TrimRight(config.Instance().DB.DBoutpath, "/"), filename)
	file.MkDir(outDir)
	outFile = fmt.Sprintf("%s/%s.sql", outDir, filename)
	outConfig := fmt.Sprintf("%s/config.toml", outDir)
	lf, err := os.OpenFile(outFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0660)
	if err != nil {
		return "", err
	}
	lc, err := os.OpenFile(outConfig, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0660)
	if err != nil {
		return "", err
	}
	defer func() {
		// 关闭文件
		lf.Close()
		lc.Close()

		// outZipFile := fmt.Sprintf("%s/%s_%s.zip", strings.TrimRight(config.Instance().DB.DBoutpath, "/"), config.Instance().DB.DBName, filename)
		//err = archiver.Archive([]string{outFile}, outZipFile)  //压缩

	}()

	// 写入头部信息
	// 	_, err = lf.WriteString(fmt.Sprintf(`/*生成时间: %s*/
	//  `, time.Now().Format("2006-01-02 15:04:05")))
	// 	if err != nil {
	// 		return "", err
	// 	}

	// 导出数据对象
	tplSqlModel := make([]*TPLSqlModel, 0)
	// 循环表名，查询出对应的表创建语句
	for _, table := range tables {
		// 导出建表语句
		sql, err := getCreateTableSQL(table)
		if err != nil {
			return "", err
		}
		tplSqlModel = append(tplSqlModel, &TPLSqlModel{
			TableName: table,
			CreateSQL: sql,
		})

		// 写入一个表到建表语句
		_, err = lf.WriteString(fmt.Sprintf(
			`%s-- ----------------------------
 -- 表结构 %s
 -- ----------------------------
SET FOREIGN_KEY_CHECKS = 0%s 
SET sql_mode='NO_ENGINE_SUBSTITUTION'%s
DROP TABLE IF EXISTS %s%s%s%s
 %s%s
 %s`,
			"\n\n",
			table,
			SplitStr,
			SplitStr,
			"`",
			table,
			"`",
			SplitStr,
			sql,
			SplitStr,
			"\n"))
		if err != nil {
			return "", err
		}

		// 导出数据
		if isExportdata {
			if err := exportData(lf, table); err != nil {
				return outFile, err
			}
		}
	}

	// 写入底部信息
	_, err = lf.WriteString("\nSET FOREIGN_KEY_CHECKS = 1" + SplitStr)
	if err != nil {
		return "", err
	}

	//写入配置信息
	_, err = lc.WriteString(fmt.Sprintf(`name = '%s'
desc = '%s'
type = '%s'
createtime = '%s'`, filename, desc, type_, time.Now().Format("2006-01-02 15:04:05")))
	if err != nil {
		return "", err
	}

	return outFile, nil
}

// GetCreateTableSQL 查询创建表语句
func getCreateTableSQL(tableName string) (string, error) {
	var creates CreateTable
	err := Instance().Raw(fmt.Sprintf("show create table %s", tableName)).Scan(&creates).Error

	if err != nil {
		return "", err
	}
	return creates.CreateTable, nil
}

// ExportData 导出数据为
func exportData(w io.Writer, tableName string) (err error) {
	//log.Println("开始导出数据:", tableName)
	// 查询总数据行数
	var count int64
	Instance().Table(tableName).Count(&count)

	var tableinfo []TabelInfo
	Instance().Raw(fmt.Sprintf("desc %s", tableName)).Scan(&tableinfo)
	var columns []string
	var columnspoint []string
	for _, v := range tableinfo {
		columns = append(columns, v.Field)
		columnspoint = append(columnspoint, fmt.Sprintf("`%s`", v.Field))
	}

	var offset int64
	//每次1000条
	var pagesize int64 = 1000
	for offset = 0; offset < count; offset += pagesize {
		columnspoints := strings.Join(columnspoint, ",")

		sql := fmt.Sprintf("select %s from %s limit %d offset %d", columnspoints, tableName, pagesize, offset)
		list, err := QueryMap(sql)

		if err != nil {
			return err
		}

		for _, one := range list {
			// 拼接插入语句头部
			installSQL := fmt.Sprintf("\nINSERT INTO %s (%s) VALUES ",
				tableName, columnspoints)
			values := make([]string, 0)
			for _, column := range columns {

				val, ok := one[column] // 读取本行值

				if !ok {
					return errors.New("列名和值无法对应")
				}
				if val == nil {
					val = ""
				} else {
					if valByte, ok := val.([]byte); ok {
						val = string(valByte)
					} else {
						val = fmt.Sprint(val)
					}
				}

				values = append(values, fmt.Sprintf("'%v'", val))

			}
			// 拼接插入语句值部分
			installSQL = fmt.Sprintf("%s (%s)%s", installSQL, strings.Join(values, ","), SplitStr)
			// 写入数据
			_, err = io.WriteString(w, installSQL)
			if err != nil {
				return err
			}
		}

	}
	return nil
}
